﻿#if NET35 
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Common.Logging;
using Wicresoft.Core.Osgi.Internal;

namespace Wicresoft.Core.Osgi
{
    /// <summary>
    /// OSGI配置引擎
    /// <para>2011/3/4</para>
    /// 	<para>TANGJINGBO</para>
    /// 	<author>tangjingbo</author>
    /// </summary>
    public sealed class OsgiEngine
    {
        private static ILog log = LogManager.GetLogger("OsgiEngine");
        /// <summary>
        /// 获取当前引擎单例
        /// </summary>
        /// <value>The current.</value>
        public static OsgiEngine Current { get; private set; }

        /// <summary>
        /// 根物理路径
        /// </summary>
        public string RootPath { private set; get; }

        /// <summary>
        /// 程序集库路径
        /// </summary>
        public string LibPath { get; private set; }

        /// <summary>
        /// Gets or sets the runtime path.
        /// </summary>
        /// <value>The runtime path.</value>
        public string RuntimePath { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether [disable ext attr load exception].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [disable ext attr load exception]; otherwise, <c>false</c>.
        /// </value>
        public bool DisableExtAttrLoadException { get; set; }

        /// <summary>
        /// 主程序集类Type
        /// </summary>
        public Type Clazz { get; private set; }

        private readonly Dictionary<Type, ExtensionPoint> _extensionPoints;
        private readonly Dictionary<string, MultiVersionAssembly> _assemblys;

        /// <summary>
        /// Initializes a new instance of the <see cref="OsgiEngine"/> class.
        /// </summary>
        /// <param name="rootPath">The root path.</param>
        /// <param name="runtimePath">The runtime path.</param>
        /// <param name="libPath">The lib path.</param>
        /// <param name="clazz">The clazz.</param>
        public OsgiEngine(string rootPath, string runtimePath, string libPath, Type clazz)
            : this(rootPath, runtimePath, libPath, clazz, false)
        {
        }

        /// <summary>
        /// OSGI 引擎
        /// </summary>
        /// <param name="rootPath">The root path.</param>
        /// <param name="runtimePath">The runtime path.</param>
        /// <param name="libPath">程序集库路径</param>
        /// <param name="clazz">主程序集类Type</param>
        /// <param name="disableExtAttrLoadException">if set to <c>true</c> [disable ext attr load exception].</param>
        public OsgiEngine(string rootPath, string runtimePath, string libPath, Type clazz,
                              bool disableExtAttrLoadException)
        {
            RootPath = rootPath;
            DisableExtAttrLoadException = disableExtAttrLoadException;
            RuntimePath = runtimePath;
            LibPath = libPath;
            Clazz = clazz;
            _extensionPoints = new Dictionary<Type, ExtensionPoint>();
            _assemblys = new Dictionary<string, MultiVersionAssembly>();
            InitAssembly(Clazz.Assembly);
            if (!String.IsNullOrEmpty(runtimePath))
            {
                var dllFiles = Directory.GetFiles(runtimePath, "*.dll");
                foreach (string dllFile in dllFiles)
                {
                    Assembly assembly = null;
                    try
                    {
                        assembly = Assembly.LoadFrom(dllFile);
                    }
                    catch (BadImageFormatException e)
                    {
                        log.Error("配置引擎无法加载DLL：" + dllFile, e);
                        continue;
                    }
                    if (assembly != null) InitAssembly(assembly);
                }
            }
            if (!string.IsNullOrEmpty(libPath) && Directory.Exists(libPath))
            {
                var dllFiles = Directory.GetFiles(libPath, "*.dll");
                foreach (string dllFile in dllFiles)
                {
                    Assembly assembly = null;
                    try
                    {
                        assembly = Assembly.LoadFrom(dllFile);
                    }
                    catch (BadImageFormatException e)
                    {
                        log.Error("配置引擎无法加载DLL：" + dllFile, e);
                        continue;
                    }
                    if (assembly != null) InitAssembly(assembly);
                }
            }
            var extensions = new HashSet<RegExtensionAttributeItem>();
            foreach (string key in _assemblys.Keys)
            {
                DetectExtensionPointAndRegExtensionAttr(_assemblys[key].LatestVersionAssembly, extensions);
            }
            foreach (RegExtensionAttributeItem extension in extensions)
            {
                if (_extensionPoints.ContainsKey(extension.ExtensionAttribute.GetType()))
                {
                    _extensionPoints[extension.ExtensionAttribute.GetType()].Add(extension);
                }
                else
                {
                    log.Warn(string.Format("程序集'{0}',没有找到扩展'{1}'没有对应的管理类", extension.Assembly.FullName, extension.ExtensionAttribute.GetType().FullName));
                }
            }

            foreach (var point in _extensionPoints.Values)
                point._SortAndRegisterAll();

            Current = this;
            foreach (var extension in _extensionPoints.Values)
            {
                extension.OnInit();
            }
            foreach (var extension in _extensionPoints.Values)
            {
                extension.OnLoad();
            }
        }

        private void InitAssembly(Assembly assembly)
        {
            AssemblyName assemblyName = assembly.GetName();
            MultiVersionAssembly verAssembly;
            if (_assemblys.ContainsKey(assemblyName.Name))
            {
                verAssembly = _assemblys[assemblyName.Name];
            }
            else
            {
                verAssembly = new MultiVersionAssembly();
                _assemblys[assemblyName.Name] = verAssembly;
            }
            if (verAssembly.IsLatestVersion(assembly))
            {
                verAssembly.Add(assembly);
            }
        }


        private void DetectExtensionPointAndRegExtensionAttr(Assembly assembly, HashSet<RegExtensionAttributeItem> extensions)
        {
            try
            {
                var extensionPointAttrs = assembly.GetCustomAttributes(typeof(RegExtensionPointAttribute), true)
                                          as RegExtensionPointAttribute[];
                if (extensionPointAttrs != null)
                    foreach (RegExtensionPointAttribute extensionPointAttr in extensionPointAttrs)
                    {
                        _extensionPoints[extensionPointAttr.AttributeType] = extensionPointAttr.ExtensionPoint;
                    }
                var extensionAttrs = assembly.GetCustomAttributes(typeof(RegExtensionAttribute), true)
                                     as RegExtensionAttribute[];
                if (extensionAttrs != null)
                    foreach (RegExtensionAttribute extensionAttr in extensionAttrs)
                    {
                        if (_extensionPoints.ContainsKey(extensionAttr.GetType()))
                        {
                            _extensionPoints[extensionAttr.GetType()].Add(new RegExtensionAttributeItem(assembly, extensionAttr));
                        }
                        else if (extensions != null)
                        {
                            extensions.Add(new RegExtensionAttributeItem(assembly, extensionAttr));
                        }
                    }
            }
            catch (Exception ex)
            {
                log.Error("OSGI 引擎调用RegisterAssembly(Assembly assembly, HashSet<ExtensionAttributeItem> extensions)方法异常", ex);
                if (!DisableExtAttrLoadException)
                    throw ex;
            }
        }

        /// <summary>
        /// 扩展项
        /// <para>2011/3/4</para>
        /// 	<para>TANGJINGBO</para>
        /// 	<author>tangjingbo</author>
        /// </summary>
        internal sealed class RegExtensionAttributeItem
        {
            /// <summary>
            /// 扩展项所属程序集
            /// </summary>
            /// <value>The assembly.</value>
            public Assembly Assembly { get; private set; }
            /// <summary>
            /// 获取扩展标记
            /// </summary>
            /// <value>The extension attribute.</value>
            public RegExtensionAttribute ExtensionAttribute { get; private set; }

            /// <summary>
            /// 扩展项构造
            /// </summary>
            /// <param name="assembly">The assembly.</param>
            /// <param name="extensionAttribute">The extension attribute.</param>
            public RegExtensionAttributeItem(Assembly assembly, RegExtensionAttribute extensionAttribute)
            {
                Assembly = assembly;
                ExtensionAttribute = extensionAttribute;
            }
        }
    }
}
#endif